golang tcp客户端断开自动恢复实现(附编译为dll,C#调用)

您所在的位置:网站首页 modbus tcp server如何知道客户端断开 golang tcp客户端断开自动恢复实现(附编译为dll,C#调用)

golang tcp客户端断开自动恢复实现(附编译为dll,C#调用)

2024-07-08 05:41| 来源: 网络整理| 查看: 265

package main import ( "bufio" "fmt" "net" "os" "time" ) func main(){ //开一个goroutine,做连接,并启动接收,如果连接断开,接收报错,则尝试恢复连接 go Link() //来一个goroutine,做心跳,如果不发心跳,拔掉网线不会触发接收异常 go BeatHeart() //在启动的goroutine中做发送操作 input := bufio.NewScanner(os.Stdin) for{ input.Scan() str := input.Text() fmt.Println("你输入的是:", str) sendBuf := []byte(str) if conn == nil{ fmt.Println("conn is nil") continue } n, err := conn.Write(sendBuf[:]) if err != nil { fmt.Println(err) }else{ fmt.Println("send:", n) } } } //全局tcp连接对象 var conn net.Conn //负责连接以及连接恢复 func Link(){ hostInfo := "192.168.1.100:9090" for{ var err error conn, err = net.Dial("tcp", hostInfo) fmt.Print("connect (") if err != nil { fmt.Print(") fail") }else{ fmt.Println(") ok") defer func() { conn.Close() conn = nil }() doTask(conn) } time.Sleep(3 * time.Second) } } //心跳 每8秒发送一个包 func BeatHeart(){ for{ if conn!=nil{ conn.Write([]byte("beatHeart")) } time.Sleep(time.Second * 8) } } //接收 func doTask(conn net.Conn) { var buf [128]byte for{ conn.SetReadDeadline(time.Now().Add(time.Second * 10)) n , err := conn.Read(buf[:]) if err != nil{ fmt.Println("接收错误,进行重连:", err) break } fmt.Println("接收到:", string(buf[:n])) } }

没有给全局变量加锁,正式项目需要加上。这里只维护一个tcp客户端对象,如果改造成dll,需要多个客户端对象的话,则需要设置一个字典或数组,进行维护。并在连接断开、连接恢复的时候发送通知。代码很简洁......

 

服务端简单实现:

package main import ( "fmt" "net" ) func main(){ listener, err := net.Listen("tcp", "0.0.0.0:9090") if err != nil{ fmt.Printf("listen fail, err:%v\n", err) return } for{ conn, err := listener.Accept() if err != nil{ fmt.Printf("accept fail, err:%v\n", err) continue } go process(conn) } } func process(conn net.Conn){ defer func() { conn.Close() fmt.Println("连接断开...") }() for{ var buf [128]byte n, err := conn.Read(buf[:]) if err != nil{ fmt.Printf("read from connect failed, err:%v\n", err) break } str := string(buf[:n]) fmt.Printf("receive from client, data:%v\n", str) conn.Write(buf[:n]) } }

改为dll,并在C#中进行调用:

//自动恢复Tcp客户端封装 package TcpClient import ( "fmt" "net" "time" ) type AutoRepairTcpClient struct { Conn net.Conn ServerIp string Port int NotifyFun func(id int) ReceiveFun func(data []byte) } func (obj *AutoRepairTcpClient) SetAddr(ip string, port int){ obj.ServerIp = ip obj.Port = port } func (obj *AutoRepairTcpClient)Start(){ go obj.Link() go obj.BeatHeart() } func (obj *AutoRepairTcpClient)Link(){ hostInfo := fmt.Sprintf("%s:%d", obj.ServerIp, obj.Port) for{ conn, err := net.Dial("tcp", hostInfo) fmt.Print("connect()") if err != nil{ fmt.Print("fail!\n") obj.NotifyFun(0) }else{ fmt.Print("Ok\n") obj.Conn = conn obj.NotifyFun(1) defer func(){ obj.Conn.Close() obj.Conn = nil }() obj.ReceiveMsg() } time.Sleep(3 * time.Second) } } func (obj *AutoRepairTcpClient)BeatHeart(){ for{ if obj.Conn!=nil{ obj.Conn.Write([]byte("beatHeart")) } time.Sleep(time.Second * 8) } } func (obj *AutoRepairTcpClient)ReceiveMsg() { var buf [128]byte for{ obj.Conn.SetReadDeadline(time.Now().Add(time.Second * 10)) n , err := obj.Conn.Read(buf[:]) if err != nil{ fmt.Println("接收错误,进行重连:", err) obj.NotifyFun(0) break } fmt.Println("接收到:", string(buf[:n])) obj.ReceiveFun(buf[:n]) } } func (obj *AutoRepairTcpClient)Write(buf []byte) bool { if obj.Conn == nil{ return false } n, err := obj.Conn.Write(buf) if err != nil { fmt.Println(err) return false }else{ fmt.Println("send:", n) return true } }

dll函数:

package main import "C" import ( "fmt" ) import ( "./TcpClient" "./github.com/lxn/win" "unsafe" ) //export FuncType type FuncType func() //export PrintBye func PrintBye(){ fmt.Println("From dll:Bye!") } //export Sum func Sum(a int, b int) int{ return a + b; } var _handle uintptr //export SetMainHandle func SetMainHandle(handle uintptr){ _handle = handle } //export Echo func Echo(msg string){ fmt.Println(msg) var a [10]byte for i := 0; i < 10 ; i++ { a[i] = byte(i) } wparam := &a[0] win.SendMessage(win.HWND(_handle), uint32(win.WM_USER + 1000), uintptr(unsafe.Pointer(wparam)), 10) } //export BytesTest func BytesTest(data []byte){ for i := 0;i < len(data) ;i++ { fmt.Println(data[i]) } } //export funVar var funVar func(key string) var tcpClient *TcpClient.AutoRepairTcpClient //export StartTcpClient func StartTcpClient(ip string, port int) { tcpClient = &TcpClient.AutoRepairTcpClient{} tcpClient.NotifyFun = func(id int) { win.SendMessage(win.HWND(_handle), uint32(win.WM_USER + 1001), uintptr(id), 0) } tcpClient.ReceiveFun = func(data []byte) { fmt.Println(data) fmt.Println(string(data)) var a [128]byte for i := 0; i < len(data); i++ { a[i] = data[i] } w := &a[0] win.SendMessage(win.HWND(_handle), uint32(win.WM_USER + 1002), uintptr(unsafe.Pointer(w)), uintptr(len(data))) } tcpClient.SetAddr(ip, port) tcpClient.Start() } //export Send func Send(data []byte){ fmt.Println(data) tcpClient.Write(data[:]) } func main(){ // Need a main function to make CGO compile package as C shared library }

C#调用:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; namespace CSharpTest { public struct GoString { public string Value { get; set; } public int Length { get; set; } public static implicit operator GoString(string s) { var res = new GoString() { Value = s, Length = s.Length }; //res.Value.Append(s); return res; } public static implicit operator string(GoString s) => s.Value.ToString(); } /// /// typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; /// //[StructLayout(LayoutKind.Sequential)] public struct GoSlice { public IntPtr Data; public long Len; public long Cap; } public partial class Form1 : Form { [DllImport("exportgo.dll", EntryPoint = "PrintBye", CallingConvention = CallingConvention.Cdecl)] static extern unsafe void PrintBye(); [DllImport("exportgo.dll", EntryPoint = "Sum", CallingConvention = CallingConvention.Cdecl)] static extern unsafe int Sum(int a, int b); [DllImport("exportgo.dll", EntryPoint = "Echo", CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)] static extern unsafe void Echo(GoString msg); [DllImport("exportgo.dll", EntryPoint = "SetMainHandle", CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)] static extern unsafe void SetMainHandle(UIntPtr handle); //extern void BytesTest(GoSlice p0); [DllImport("exportGo.dll", EntryPoint = "BytesTest", CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)] static extern unsafe void BytesTest(GoSlice slice); //extern void StartTcpClient(GoString p0, GoInt p1); [DllImport("exportgo.dll", EntryPoint = "StartTcpClient", CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)] static extern unsafe void StartTcpClient(GoString ip, int port); //extern void Send(GoSlice p0); [DllImport("exportgo.dll", EntryPoint = "Send", CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)] static extern unsafe void Send(GoSlice p0); [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate void CallBackFun(); public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { PrintBye(); Debug.WriteLine(Sum(22, 33)); } private void button2_Click(object sender, EventArgs e) { CallBackFun f = new CallBackFun(callback); var p = Marshal.GetFunctionPointerForDelegate(f); Echo("agc"); } private void callback() { Debug.WriteLine(""); } private void Form1_Load(object sender, EventArgs e) { SetMainHandle((UIntPtr)(Handle.ToInt64())); } protected override void WndProc(ref Message m) { base.WndProc(ref m); if (m.Msg == 1024 + 1000) { var bytes = new byte[(int)m.LParam]; Marshal.Copy(m.WParam, bytes, 0, (int)m.LParam); MessageBox.Show("go 触发..." + m.LParam + " " + bytes.Aggregate("", (sum, i) => sum = sum + "," + i.ToString())); } else if (m.Msg == 1024 + 1001) { var type = (int)m.WParam; ShowLog(type == 0 ? "通信故障" : "通信恢复"); } if (m.Msg == 1024 + 1002) { var bytes = new byte[(int)m.LParam]; Marshal.Copy(m.WParam, bytes, 0, (int)m.LParam); var str = Encoding.UTF8.GetString(bytes); MessageBox.Show("接收到..." + str); } } //byte[]转换为Intptr public static IntPtr BytesToIntptr(byte[] bytes) { int size = bytes.Length; IntPtr buffer = Marshal.AllocHGlobal(size); try { Marshal.Copy(bytes, 0, buffer, size); return buffer; } finally { //Marshal.FreeHGlobal(buffer); } } private void button3_Click(object sender, EventArgs e) { var p = new GoSlice { Len = 3, Cap = 3 }; var data = new byte[3]; data[0] = 2; data[1] = 3; data[2] = 4; p.Data = BytesToIntptr(data); BytesTest(p); Marshal.FreeHGlobal(p.Data); } private void button4_Click(object sender, EventArgs e) { GoString addr = new GoString { Value = "127.0.0.1", Length = 9 }; StartTcpClient(addr, 9090); } private void button5_Click(object sender, EventArgs e) { var p = new GoSlice { Len = 3, Cap = 3 }; var data = Encoding.UTF8.GetBytes("hello world"); p.Len = data.Length; p.Cap = data.Length; p.Data = BytesToIntptr(data); Send(p); Marshal.FreeHGlobal(p.Data); } private void ShowLog(string log) { BeginInvoke(new Action(() => { textBox1.AppendText(log + "\r\n"); })); } } }



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3